package org.advanced.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author John.zhang
 * @version Id: NioSelectorServer, v 1.0 2020/12/17 13:42 ZhiYong Exp $
 * 基于多路复用器来实现服务端的连接处理
 * <p>
 * configureBlocking必须设置为非阻塞
 * fileChannel不适合selector,fileChannel不适合selector不能切换为非阻塞
 */
public class NioSelectorServer {

    /**
     * 管理所有连接的注册中心
     */
    static Selector selector;

    public static void main(String[] args) {
        try {
            selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //多路复用模式下必须设置成非阻塞(这里是设置IO的非阻塞-基于多路的设置，所以是IO)
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            //监听连接事件
            SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                //阻塞(可以用一个线程来设计解决高并发问题)
                selector.select();
                //所有的注册到复路器上面的channel
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    //迭代器不会处理移除事件，避免重复处理
                    iterator.remove();
                    //连接事件
                    if (next.isAcceptable()) {
                        handleAccept(next);
                        //读事件
                    } else if (next.isReadable()) {
                        handleRead(next);
                    }
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void wakeup() {
        //唤醒处于select阻塞状态下的线程
        selector.wakeup();
    }

    /**
     * 处理连接事件
     *
     * @param selectionKey
     */
    private static void handleAccept(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
        //这里一定会有连接进来，经过了上面的阻塞过程，这里是肯定是就绪的
        SocketChannel socketChannel = channel.accept();
        //设置连接的非阻塞
        socketChannel.configureBlocking(false);
        socketChannel.write(ByteBuffer.wrap("Hello client I'm NIO Server with Selector".getBytes()));
        //继续注册socketChannel的读事件，经由上面的处理转到下面handleRead的读事件来处理
        socketChannel.register(selector, SelectionKey.OP_READ);
    }

    /**
     * 处理读事件
     *
     * @param selectionKey
     */
    private static void handleRead(SelectionKey selectionKey) throws IOException {
        //经过上面的 write 并且 register 这里拿到的就是一个度事件的通道而并非连接
        SocketChannel channel = (SocketChannel) selectionKey.channel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        channel.read(byteBuffer);
        System.out.println("server receive Msg:" + new String(byteBuffer.array()));
    }
}
